Saltar al contenido principal

ULedger Virtual Machine

La ULedger Virtual Machine (ULVM) te permite escribir smart contracts. Los contratos se compilan a WebAssembly (WASM), y la ULVM expone un pequeño conjunto de funciones host que tu contrato puede invocar para:

  • leer/escribir el estado del blockchain (almacenamiento clave–valor)
  • registrar mensajes
  • emitir eventos
  • acceder al contexto del contrato (llamante, propietario, altura de bloque)

Modelo de estado: almacenamiento clave–valor

ULedger almacena el estado del contrato como un mapa clave–valor en el Blockchain. Tu contrato lee y escribe valores por clave.

Conceptualmente, el estado del contrato se ve así:

{
"state": {
"key1": "value1",
"key2": "value2"
}
}

Entonces, cuando llamas a set_storage("my-key", someValue).


Tipos de datos soportados

Dado que el estado se serializa, la ULVM soporta un conjunto fijo de tipos (a través del serializador que codifica/decodifica valores):

Null = 0,
Bool = 1,
Int32 = 2,
Int64 = 3,
String = 4,
Bytes = 5,
Array = 6,
Map = 7,

Tus métodos auxiliares (como storeInt, retrieveInt, etc.) utilizan este serializador internamente para que no tengas que codificar/decodificar bytes manualmente.


Funciones host de la ULVM (imports)

En AssemblyScript, las funciones host de la ULVM son (a alto nivel):

  • get_storage: obtiene los bytes del valor para una clave
  • set_storage: almacena bytes de valor bajo una clave
  • delete_storage: elimina una clave del estado
  • has_key: verifica si existe una clave
  • log: escribe un mensaje de registro
  • emit_event: emite un evento con un nombre y un payload de datos
  • get_caller: la cuenta/wallet que está llamando a este contrato
  • get_owner: el propietario del contrato
  • get_block_height: altura de bloque actual

Estas funciones toman punteros (usize) porque WASM pasa datos a través de memoria lineal. En la práctica, normalmente llamarás a utilidades envolventes (como storeInt) que gestionan punteros y serialización por ti.


Por qué tipado fuerte (AssemblyScript)

Tus contratos se escriben en AssemblyScript (sintaxis similar a TypeScript compilada a WASM). Debido a que compila a WebAssembly, usarás tipos explícitos como i32, i64, bool, Array<i32>, etc.


Tutorial: Almacenar y recuperar (ejemplo básico)

Este primer ejemplo muestra el flujo más sencillo:

  1. almacenar un i32 bajo una clave
  2. recuperarlo

Paso 1: importar los helpers de la ULVM

import {
storeInt,
retrieveInt,
storeIntArray,
retrieveIntArray,
} from "../src/ulvm";

Estos helpers son envoltorios delgados alrededor de env.get_storage / env.set_storage, más la serialización.


Paso 2: escribir el contrato

// The entry file of your WebAssembly module.
import {
storeInt,
retrieveInt,
storeIntArray,
retrieveIntArray,
} from "../src/ulvm";

/**
* Simple direct test function that stores a value and retrieves it.
* Uses a fixed key to keep the example minimal.
*/
export function testStoreAndRetrieve(value: i32): i32 {
const key = "test-key";

// Store the value
storeValue(key, value);

// Retrieve and return the value
return retrieveValue(key);
}

/**
* Store a value in storage with a string key
*/
export function storeValue(key: string, value: i32): void {
storeInt(key, value);
}

/**
* Retrieve a value from storage by string key
*/
export function retrieveValue(key: string): i32 {
return retrieveInt(key);
}

/**
* Test storing and retrieving an array
*/
export function testStoreAndRetrieveArray(): i32 {
const key = "array-key";
const array = [10, 20, 30, 40, 50];

storeArray(key, array);

const retrieved = retrieveArray(key);

// Return the length to verify it worked
return retrieved.length;
}

/**
* Store an array with a string key
*/
export function storeArray(key: string, values: Array<i32>): void {
storeIntArray(key, values);
}

/**
* Retrieve an array by string key
*/
export function retrieveArray(key: string): Array<i32> {
return retrieveIntArray(key);
}

Qué hace este contrato

testStoreAndRetrieve(value: i32) -> i32

  • escribe value en el almacenamiento bajo "test-key"
  • lee "test-key" de vuelta
  • devuelve el valor recuperado

Entonces, si llamas a testStoreAndRetrieve(123), tu estado termina conceptualmente así:

{
"state": {
"test-key": 123
}
}

testStoreAndRetrieveArray() -> i32

  • almacena [10,20,30,40,50] bajo "array-key"
  • lo recupera de vuelta
  • devuelve la longitud del array (5)

Compilación: compilar AssemblyScript a WebAssembly

Si estás usando la estructura de proyecto estándar de AssemblyScript, el flujo típico es:

1) Instalar AssemblyScript (dependencia de desarrollo)

npm i -D assemblyscript

2) Coloca el punto de entrada de tu contrato en assembly/index.ts

Por ejemplo:

  • assembly/smart-contract.ts (tu archivo de contrato)

3) Compilar a .wat y .wasm

Opción A: comando directo asc

npx asc assembly/smart-contract.ts -o smart-contract.wasm --textFile smart-contract.wat

Resultado: tendrás un archivo .wat (ejemplo: smart-contract.wat) que es un archivo de texto que el SDK de ULedger puede desplegar.


Ejecución: Despliegue

Para desplegar el Smart Contract compilado puedes usar el SDK de ULedger.

Para la guía en Go sobre cómo hacer esto, consulta la documentación de Despliegue de Smart Contracts.